#include "common.h"


int get_addr(char *dst, struct sockaddr *addr)
{
	struct addrinfo *res;
	int ret = -1;
	ret = getaddrinfo(dst, NULL, NULL, &res);
	if (ret) {
		rdma_error("获取主机地址失败, 无效的IP或主机名!\n");
		return ret;
	}
	memcpy(addr, res->ai_addr, sizeof(struct sockaddr_in));
	freeaddrinfo(res);
	return ret;
}



void show_meta(struct meta_attr *attr){
	if(!attr){
		rdma_error("Passed attr is NULL\n");
		return;
	}
	printf("---------------------------------------------------------\n");
	printf("内存属性, 地址: %p, 大小: %u, 本地访问秘钥 : 0x%x \n", 
			(void*) attr->address, (unsigned int) attr->length,
			attr->key.lkey);
	printf("---------------------------------------------------------\n");
}



int cm_event_callback(struct rdma_event_channel *channel, 
					enum rdma_cm_event_type expected_event,
					struct rdma_cm_event **cm_event)
{
	int ret = 1;
	ret = rdma_get_cm_event(channel, cm_event);
	if (ret) {
		rdma_error("获取CM事件失败, 错误码: %d \n", -errno);
		return -errno;
	}
	
	// 获取成功，到那时状态不希望的结果
	if(0 != (*cm_event)->status){
		rdma_error("CM事件返回非0状态值: %d\n", (*cm_event)->status);
		ret = -((*cm_event)->status);
		//! 感谢该事件(本质是释放该事件的内存资源)
		rdma_ack_cm_event(*cm_event);
		return ret;
	}

	// 如果未获取到期望的事件类型
	if ((*cm_event)->event != expected_event) {
		rdma_error("以外的事件结果: %s [ 期望事件: %s ]", 
				rdma_event_str((*cm_event)->event),
				rdma_event_str(expected_event));
		rdma_ack_cm_event(*cm_event);
		return -1;
	}
	rdma_debug("收到新的期望的事件类型: %s \n", 
			rdma_event_str((*cm_event)->event));
	return ret;
}



struct ibv_mr* mr_alloc(struct ibv_pd *pd, uint32_t size,
						enum ibv_access_flags permission) 
{
	struct ibv_mr *mr = NULL;
	if (!pd) {
		rdma_error("PD保护域为空!\n");
		return NULL;
	}
	void *buf = calloc(1, size);
	if (!buf) {
		rdma_error("内存不足分配失败, -ENOMEM\n");
		return NULL;
	}
	rdma_debug("内存分为配在: %p , 大小为: %u \n", buf, size);
	mr = mr_register(pd, buf, size, permission);
	if(!mr){
		free(buf);
	}
	return mr;
}



void mr_free(struct ibv_mr *mr) 
{
	if (!mr) {
		rdma_error("传入空的MR, 忽略...\n");
		return ;
	}
	void *to_free = mr->addr;
	mr_deregister(mr);
	rdma_debug("释放MR内存地址: %p\n", to_free);
	free(to_free);
}



struct ibv_mr *mr_register(struct ibv_pd *pd, void *addr, 
		uint32_t length, enum ibv_access_flags permission)
{
	struct ibv_mr *mr = NULL;
	if (!pd) {
		rdma_error("保护域为空, 忽略该注册行为...\n");
		return NULL;
	}
	mr = ibv_reg_mr(pd, addr, length, permission);
	if (!mr) {
		rdma_error("创建MR失败, 错误码: %d \n", -errno);
		return NULL;
	}
	rdma_debug("MR成功注册: %p , 内存大小: %u , 访问秘钥: 0x%x \n", 
			mr->addr,  (unsigned int) mr->length, mr->lkey);
	
	return mr;
}



void mr_deregister(struct ibv_mr *mr) 
{
	if (!mr) { 
		rdma_error("传入空的MR, 忽略...\n");
		return;
	}
	rdma_debug("反注册MR: %p , len: %u , key : 0x%x \n", 
		mr->addr, (unsigned int) mr->length, mr->lkey);
	ibv_dereg_mr(mr);
}




int wc_event_callback (struct ibv_comp_channel *channel, 
					struct ibv_wc *wc, int max_wc)
{
	struct ibv_cq *cq = NULL;
	void *context     = NULL;
	int ret = -1, i, total_wc = 0;

	// 等待CQ管道的事件通知
	ret = ibv_get_cq_event(channel,  // 我们希望等待CQ事件通知的
		       &cq,                  // 活动的CQ事件(需要对应之前创建的Post Send和Recv请求)
		       &context);            // CQ相关的用户上下文信息
   
	if (ret) {
		rdma_error("获取CQ失败, 错误码: %d \n", -errno);
		return -errno;
	}


   // 从CQ中获取详细信息
	ret = ibv_req_notify_cq(cq, 0);
	if (ret){
		rdma_error("获取CQ详细信息失败, 错误码: %d \n", -errno);
		return -errno;
	}


	total_wc = 0;
    do {
		// CQ中获取CQE
		ret = ibv_poll_cq(cq /* the CQ, we got notification for */, 
				max_wc - total_wc /* number of remaining WC elements*/,
				wc + total_wc/* where to store */
			);
		if (ret < 0) {
			rdma_error("从CQ拉取WC失败, 错误码: %d \n", ret);
			return ret;
		}
		total_wc += ret;
    } while (total_wc < max_wc); 
    rdma_debug("已完成 %d WCs.\n", total_wc);


    // 检验IO WC的有效性
    for( i = 0 ; i < total_wc ; i++) {
        if (wc[i].status != IBV_WC_SUCCESS) {
	        rdma_error("WC[%d]错误码: %s.", i, 
		   			ibv_wc_status_str(wc[i].status));
	        return -(wc[i].status);
        }
    }
   
	// 我们这里只有给CQ
	ibv_ack_cq_events(cq, 1);
	return total_wc; 
}




void log_cm_id(struct rdma_cm_id *id)
{
	if(!id){
		rdma_error("CM id为空, 忽略...\n");
		return;
	}

	if(id->verbs && id->verbs->device)
		printf("DRMA设备上下文地址: %p (设备名: %s) \n", 
				id->verbs, id->verbs->device->name);
	
	if(id->channel)
		printf("CM事件管道: %p\n", id->channel);

	printf("QP: %p, 端口空间: %x, 端口数: %u \n", 
			id->qp, id->ps, id->port_num);
}
